/*
    Licensed to the Apache Software Foundation (ASF) under one
    or more contributor license agreements.  See the NOTICE file
    distributed with this work for additional information
    regarding copyright ownership.  The ASF licenses this file
    to you under the Apache License, Version 2.0 (the
    "License"); you may not use this file except in compliance
    with the License.  You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing,
    software distributed under the License is distributed on an
    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    KIND, either express or implied.  See the License for the
    specific language governing permissions and limitations
    under the License.
 */
package erwiki.plugins;

import java.util.Map;

import org.elwiki_data.AttachmentContent;

import erwiki.api.WikiPartReference;
import erwiki.api.attachment.AttachmentManager;
import erwiki.api.core.WikiContext;
import erwiki.api.exceptions.PluginException;
import erwiki.api.exceptions.ProviderException;
import erwiki.api.plugin.WikiPlugin;
import erwiki.util.TextUtil;

/**
 * Плагин ErWiki. Предоставляет больший контроль включения изображения в страницу, чем это возможно
 * при простой инклюзии изображения.<br>
 * Большинство параметров являются эквивалентами атрибутов html-изображения.
 * <p>
 * Параметры плагина:
 * <ul>
 * <li><b>src</b> – источник (URL) изображения (обязательный параметр)</li>
 * <li><b>align</b> – выравнивание изображения</li>
 * <li><b>height</b> – высота изображения</li>
 * <li><b>width</b> – ширина изображения</li>
 * <li><b>alt</b> – альтернативный текст</li>
 * <li><b>caption</b> – подпись к изображению</li>
 * <li><b>link</b> – гиперссылка на изображение.</li>
 * <li><b>target</b> – цель (фрейм), используемая для открытия изображения.</li>
 * <li><b>style</b> – атрибут стиля изображения</li>
 * <li><b>class</b> – связанный класс изображения</li>
 * <li><b>border</b> – бордюр изображения</li>
 * <li><b>title</b> – заголовок изображения, может быть представлен в виде всплывающей
 * подсказки.</li>
 * </ul>
 */
// FIXME: Сделать внутренние ссылки на вики пока невозможно. Чтобы сделать это аккуратно, необходима модернизация TranslatorReader. 
public class ImagePlugin implements WikiPlugin {

	/** Имя параметра для настройки <i>src</i>. Значение <tt>{@value}</tt>. */
	public static final String PARAM_SRC = "src";

	/** Имя параметра для настройки <i>align</i>. Значение <tt>{@value}</tt>. */
	public static final String PARAM_ALIGN = "align";

	/** Имя параметра для настройки <i>height</i>. Значение <tt>{@value}</tt>. */
	public static final String PARAM_HEIGHT = "height";

	/** Имя параметра для настройки <i>width</i>. Значение <tt>{@value}</tt>. */
	public static final String PARAM_WIDTH = "width";

	/** Имя параметра для настройки <i>alt</i>. Значение <tt>{@value}</tt>. */
	public static final String PARAM_ALT = "alt";

	/** Имя параметра для настройки <i>caption</i>. Значение <tt>{@value}</tt>. */
	public static final String PARAM_CAPTION = "caption";

	/** Имя параметра для настройки <i>link</i>. Значение <tt>{@value}</tt>. */
	public static final String PARAM_LINK = "link";

	/** Имя параметра для настройки <i>target</i>. Значение <tt>{@value}</tt>. */
	public static final String PARAM_TARGET = "target";

	/** Имя параметра для настройки <i>style</i>. Значение <tt>{@value}</tt>. */
	public static final String PARAM_STYLE = "style";

	/** Имя параметра для настройки <i>class</i>. Значение <tt>{@value}</tt>. */
	public static final String PARAM_CLASS = "class";

	// :FVK: этот комментарий от JSPWiki: public static final String PARAM_MAP = "map";

	/** Имя параметра для настройки <i>border</i>. Значение <tt>{@value}</tt>. */
	public static final String PARAM_BORDER = "border";

	/** Имя параметра для настройки <i>title</i>. Значение <tt>{@value}</tt>. */
	public static final String PARAM_TITLE = "title";

	@WikiPartReference
	private AttachmentManager attachmentManager;

	/**
	 * {@inheritDoc}
	 */
	@Override
	public String execute(WikiContext context, Map<String, String> params) throws PluginException {
		/* Разбор параметров.
		 */
		String src = getCleanParameter(params, PARAM_SRC);
		String align = getCleanParameter(params, PARAM_ALIGN);
		String ht = getCleanParameter(params, PARAM_HEIGHT);
		String wt = getCleanParameter(params, PARAM_WIDTH);
		String alt = getCleanParameter(params, PARAM_ALT);
		String caption = getCleanParameter(params, PARAM_CAPTION);
		String link = getCleanParameter(params, PARAM_LINK);
		String target = getCleanParameter(params, PARAM_TARGET);
		String style = getCleanParameter(params, PARAM_STYLE);
		String cssclass = getCleanParameter(params, PARAM_CLASS);
		// String map = getCleanParameter( params, PARAM_MAP );
		String border = getCleanParameter(params, PARAM_BORDER);
		String title = getCleanParameter(params, PARAM_TITLE);

		if (src == null) {
			throw new PluginException("Parameter 'src' is required for Image plugin");
		}

		// if( cssclass == null ) cssclass = "imageplugin";
		// :FVK: - почему коммент? А может нормально? - т.к. этот класс ставится всегда для таблицы, ниже.

		if (target != null && !isValidTargetValue(target)) {
			target = null; // недопустимое значение, поэтому игнорировать его.
		}

		/* Выполнить актуальную работу.
		 */
		try {
			AttachmentContent att = this.attachmentManager.getAttachmentContent(src);

			if (att != null) {
				String atchId = att.getPageAttachment().getId();
				src = context.getURL(WikiContext.ATTACHMENT_DOGET, "", "&atchid=" + atchId);
			} // else {} -- TODO: добавить обработку отсутствия прикрепления. Иначе - выдается неверный URL.
		} catch (ProviderException e) {
			throw new PluginException("Attachment info failed: " + e.getMessage());
		}

		StringBuilder result = new StringBuilder("<table border=\"0\" class=\"imageplugin\"");

		if (title != null) {
			result.append(" title=\"" + title + "\"");
		}

		if (align != null) {
			if (align.equals("center")) {
				result.append(
						" style=\"margin-left: auto; margin-right: auto; text-align:center; vertical-align:middle;\"");
			} else {
				result.append(" style=\"float:" + align + ";\"");
			}
		}

		result.append(">\n");

		if (caption != null) {
			result.append("<caption>" + caption + "</caption>\n");
		}

		// move css class and style to the container of the image, so it doesn't affect the caption
		result.append("<tr><td");

		if (cssclass != null) {
			result.append(" class=\"" + cssclass + "\"");
		}

		if (style != null) {
			result.append(" style=\"" + style);

			// Удостовериться, что добавлен ";" в конец строки стиля.
			if (result.charAt(result.length() - 1) != ';')
				result.append(";");

			result.append("\"");
		}

		result.append(">");

		if (link != null) {
			result.append("<a href=\"" + link + "\"");
			if (target != null) {
				result.append(" target=\"" + target + "\"");
			}
			result.append(">");
		}

		result.append("<img src=\"" + src + "\"");

		if (ht != null) {
			result.append(" height=\"" + ht + "\"");
		}
		if (wt != null) {
			result.append(" width=\"" + wt + "\"");
		}
		if (alt != null) {
			result.append(" alt=\"" + alt + "\"");
		}
		if (border != null) {
			result.append(" border=\"" + border + "\"");
		}
		// if( map != null ) result.append(" map=\""+map+"\"");

		result.append(" />");
		if (link != null) {
			result.append("</a>");
		}
		result.append("</td></tr>\n");
		result.append("</table>\n");

		return result.toString();
	}

	/**
	 * Этот метод используется для удаления таких вещей, как кавычки, которые злоумышленник мог бы
	 * использовать для остановки обработки и вставки JavaScript.
	 */
	private String getCleanParameter(Map<String, String> params, String paramId) {
		return TextUtil.replaceEntities(params.get(paramId));
	}

	/**
	 * Проверят заданную строку на равенство одной из следующих строк:</br>
	 * "_blank", "_self", "_parent", "_top"</br>
	 * или, что строка начинается с буквы.
	 * 
	 * @param str
	 * @return Статус проверки.
	 */
	private boolean isValidTargetValue(String str) {
		if (str.equals("_blank") || str.equals("_self") || str.equals("_parent") || str.equals("_top")) {
			return true;
		} else if (str.length() > 0) { // check [a-zA-Z]
			char c = str.charAt(0);
			return Character.isLowerCase(c) || Character.isUpperCase(c);
		}
		return false;
	}

}
